Dx.declare('Dx.net.Client', {

   '@accessors': {
      
      'driver': null,

      'url': null,

      'async': true,

      'user': null,

      'password': '',

      'callback': null,

      'scope': null,

      'state': null,

      'successful': false,

      'completed': false,
      
      'aborted': false,

      'status': 0

   },

   '@actions': {

      'request': [

         'initialize',

         'validate',

         'process'

      ],

      'abort': [

         'validate',

         'process'

      ],

      'cleanup': [

         'abort',

         'purge'

      ],

      'isErrorStatus': [

         'initialize',

         'format'

      ],

      'createDriver': [

         'validate',

         'format'

      ]

   },

   '@@callback': function(className, Class, Prototype) {

      var D = Dx;

      var superPrototype = '@superclass' in Prototype &&

            Prototype['@superclass'];

      var superStatus = superPrototype && D.isObject(superPrototype.STATUS) ?

            superPrototype.STATUS : false;

      var Status = Prototype.STATUS;

      var n;

      if (!D.isObject(Status)) {

         Status = {};

         Prototype.STATUS = Status;

      }

      if (superStatus && superStatus !== Status) {

         for (n in superStatus) {

            if (superStatus.hasOwnProperty(n)) {

               Status[n] = superStatus[n];

            }

         }

      }

   },


   enabled: false,

   requesting: false,

   responseData: undefined,

   STATES: {
      'uninitialized': 'UNSENT',    // - Has not started loading yet
      'loading': 'LOADING',         // - Is loading
      'interactive': 'LOADING',     // - Has loaded enough and the user
                                    //    can interact with it
      'complete': 'DONE'            // - completed!
   },

   STATUS: {
      0: 'Uninitialized',
      100: 'Continue',
      101: 'Switching Protocols',
      200: 'OK',
      201: 'Created',
      202: 'Accepted',
      203: 'Non-Authoritative Information',
      204: 'No Content',
      205: 'Reset Content',
      206: 'Partial Content',
      300: 'Multiple Choices',
      301: 'Moved Permanently',
      302: 'Found',
      303: 'See Other',
      304: 'Not Modified',
      305: 'Use Proxy',
      306: '(Unused)',
      307: 'Temporary Redirect',
      400: 'Bad Request',
      401: 'Unauthorized',
      402: 'Payment Required',
      403: 'Forbidden',
      404: 'Not Found',
      405: 'Method Not Allowed',
      406: 'Not Acceptable',
      407: 'Proxy Authentication Required',
      408: 'Request Timeout',
      409: 'Conflict',
      410: 'Gone',
      411: 'Length Required',
      412: 'Precondition Failed',
      413: 'Request Entity Too Large',
      414: 'Request-URI Too Long',
      415: 'Unsupported Media Type',
      416: 'Requested Range Not Satisfiable',
      417: 'Expectation Failed',
      500: 'Internal Server Error',
      501: 'Not Implemented',
      502: 'Bad Gateway',
      503: 'Service Unavailable',
      504: 'Gateway Timeout',
      505: 'Protocol Version Not Supported'

   },

   startState: 'uninitialized',

   endState: 'complete',

   isRequesting: function() {

      return this.requesting && !this.completed;

   },

   isErrorStatus_initialize: function(o, status) {

      var D = Dx;

      var list = this.STATUS;

      o.stop = !D.isNumber(status) || !(status in list);

      o.returnValue = true;

   },

   isErrorStatus_format: function(o, status) {

      o.returnValue = status >= 200 && status < 300;


   },

   createDriver_validate: function(o) {

      o.returnValue = void(0);

      o.stop = !this.enabled;

   },

   setSuccessful_process: function(o, success) {

      this.successful = success !== false;

   },

   setCompleted_process: function(o, completed) {

      this.completed = completed !== false;

   },

   setState_validate: function(o, state) {

      var states = this.STATES;

      o.stop = !states.hasOwnProperty(state);

   },

   setState_process: function(o, state) {
      
      var old = this.state;

      this.state = state;
      
      // call state change
      if (this.requesting && old !== state) {
         
         this.request_stateChange(
            
            state,
            
            state == this.endState // ended
            
         );
         
      }

   },

   getState_initialize: function(o) {

      o.returnValue = this.STATES[this.startState];

   },

   getState_format: function(o) {

      o.returnValue = this.STATES[this.state];

   },

   setStatus_validate: function(o, status) {

      o.stop = !Dx.isNumber(status) || !(status in this.STATUS);

   },

   request_initialize: function(o, options, callback, scope) {

      var D = Dx;

      if (this.enabled) {

         // reset connection
         this.cleanup();

         if (D.isString(options, true)) {

            options = {

               url: options,

               callback: callback,

               scope: scope

            }

         }

         if (D.isObject(options)) {

            this.setUrl(options.url);

            if (D.isBoolean(options.async)) {

               this.setAsync(options.async);

            }

            this.setCallback(options.callback);

            this.setScope(options.scope);

            o.params[1] = options;

            return;

         }

      }

      o.stop = true;

   },

   request_validate: function(o, options) {
      
      var driver = this.createDriver();
      
      this.driver = driver;
      
      o.stop = this.requesting || !driver;

   },

   request_process: function(o, options) {

      this.requesting = true;

      this.setState(this.startState);

   },
   
   request_stateChange: function(state, ended) {
      
      // request already ended
      if (ended) {
         
         delete this.requesting;
      
         this.setCompleted();
   
         this.setSuccessful(
   
            this.isErrorStatus()
   
         );
         
         this.response_process(this.driver);
         
      }
      
   },

   response_process: function(driver) {
      
      var callback = this.getCallback();

      var scope = this.getScope();

      if (this.successful) {

         this.response_processSuccessfulRequest(driver);

      } else {

         this.response_processFailedRequest(driver);

      }

      if (callback) {

         callback.call(scope, this, this.getSuccessful());

      }

   },

   response_processSuccessfulRequest: function(driver) {

   },

   response_processFailedRequest: function(driver) {

   },

   abort_validate: function(o) {

      var completed = this.getCompleted();

      o.completed = completed;

      o.stop = !this.isRequesting();

   },

   abort_process: function(o) {
      
      // set aborted
      this.setAborted(true);

      this.cleanup_purge(o);

   },

   cleanup_abort: function(o) {

      if (this.isRequesting()) {

         o.stop = true;

         this.abort();

      }

   },

   cleanup_purge: function(o) {

      delete this.requesting;

      delete this.complete;

      delete this.driver;

      delete this.successful;

      this.setState(this.startState);

   }

});
